#include "main_ui.h"
#include "ui_main_ui.h"

#include <QVBoxLayout>
#include <QGroupBox>
#include <QFileDialog>
#include <QSpinBox>

#define dCalibrationFileName "CalibrationData.csv"


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{

    ui->setupUi(this);
    setWindowTitle(QStringLiteral("氢浓度传感器检测")); // 使用QStringLiteral优化字符串性能，尤其在频繁调用时
    hcng = new HCNG();

    connect(hcng,&HCNG::MessageHint_signal,this,&Widget::MessageHint_slot);
    connect(hcng,&HCNG::FrameCommandReceive_signal,this,&Widget::FrameCommandReceive_slot);

    //使用QVBoxLayout管理frame的布局：为了让子界面随界面自适应缩放
    QVBoxLayout *layout = new QVBoxLayout(ui->frame);
    layout->setContentsMargins(0, 0, 0, 0); // 可以根据需要设置边距

    FileTransmitWrite_user0.enable = false;
    OneClickState = false;
}

Widget::~Widget()
{
    delete hcng;
    delete ui;
}

void Widget::MessageHint_slot(QString message)//运行信息输出
{
    message += QString("\n");
    ui->cMessagePrintf->moveCursor(QTextCursor::End);//移动光标到最后一行
    ui->cMessagePrintf->insertPlainText(message); //光标后添加文本
}
/****************************************
@function:读取电脑端口函数
@param:void
@return:void
@note:
****************************************/
void Widget::Serial_ReadIPCPort(void)
{
    ui->cSerialPort->clear();
    QStringList serialNamePort;
    foreach(const QSerialPortInfo &info ,QSerialPortInfo::availablePorts())
    {
        serialNamePort<<info.portName();
    }
    ui->cSerialPort->addItems(serialNamePort);
}

QFile file;
QTextStream file_stream;

/****************************************
@function:接收帧解析
@param:
    FrameIdentify:帧标识符
    data:帧数据;
    length:数据长度
@return:void
@note:
****************************************/
void Widget::FrameCommandReceive_slot(quint8 FrameIdentify,quint8 *data,quint32 length)
{
    //dFrame_ID=0x81
    if((FrameIdentify != 0x81)\
        || (data == NULL)\
        || (length < 3))return;

    quint16 pos = 0;
    quint8 BoardAddress = 0,DeviceAddress_Master = 0;
    QString str;

    BoardAddress = *(data + pos++);
    DeviceAddress_Master = *(data + pos++);

    if(DeviceAddress_Master != HCNG::eDeviceAddress_Master)return;
    quint8 address = 0,cmd = 0,code = 0;

    address = BoardAddress;
    cmd = data[pos++];
    code = data[pos++];

    if((address == HCNG::eDeviceAddress_Broadcast) || (address == HCNG::eDeviceAddress_Master) || !hcng->FrameCommand_ValueCheck(HCNG::eDeviceAddress_Slave,(HCNG::Protocol_CommandType_EnumDef )cmd,code))
    {
        MessageHint_slot(QString("解析到异常帧:address=%1 cmd=%2 code=%3").arg(address).arg(cmd).arg(code));
        return;
    }

    switch(cmd)
    {
        case HCNG::eCommandType_Device:
        {
            switch(code)
            {
                case HCNG::eCodeType_Device_SystemReset_Ack:
                {
                    MessageHint_slot(QString("Address:%1").arg(address)+QString("号设备复位成功！"));
                }break;
                case HCNG::eCodeType_Device_SystemRestore_Ack:
                {
                    MessageHint_slot(QString("Address:%1").arg(address)+QString("号设备恢复出厂设置成功！"));
                    ui->cDeviceAddress->setText("0");
                }break;
                case HCNG::eCodeType_Device_Address_Ack:
                {
                    MessageHint_slot(QString("Address:%1").arg(address)+QString("号设备更改地址成功！")+\
                                     QString(" 当前设备Address: ")+QString("%1").arg(ui->cRunAddressText->text()));
                }break;
                case HCNG::eCodeType_Device_CalibrationDataUse_Ack:{MessageHint_slot(QString("Address:%1").arg(address)+QString("号设备校准数据设置成功！"));}break;
                case HCNG::eCodeType_Device_Sample_Ack:
                {
                    HCNG::Convert_FloatParameter_UnionDef vfloat;
                    float temperature = 0.0f,concentration = 0.0f, VoltageTemperature = 0.0f,VoltageHydrogen = 0.0f;

                    if(data == NULL)return;
                    vfloat.Memory.byte1 = *(data + pos++);
                    vfloat.Memory.byte2 = *(data + pos++);
                    vfloat.Memory.byte3 = *(data + pos++);
                    vfloat.Memory.byte4 = *(data + pos++);
                    temperature = vfloat.Value;

                    vfloat.Memory.byte1 = *(data + pos++);
                    vfloat.Memory.byte2 = *(data + pos++);
                    vfloat.Memory.byte3 = *(data + pos++);
                    vfloat.Memory.byte4 = *(data + pos++);
                    concentration = vfloat.Value;

                    vfloat.Memory.byte1 = *(data + pos++);
                    vfloat.Memory.byte2 = *(data + pos++);
                    vfloat.Memory.byte3 = *(data + pos++);
                    vfloat.Memory.byte4 = *(data + pos++);
                    VoltageTemperature = vfloat.Value;

                    vfloat.Memory.byte1 = *(data + pos++);
                    vfloat.Memory.byte2 = *(data + pos++);
                    vfloat.Memory.byte3 = *(data + pos++);
                    vfloat.Memory.byte4 = *(data + pos++);
                    VoltageHydrogen = vfloat.Value;

                    //str = QString("temperature= %1℃").arg(temperature)+QString(" concentration= %1").arg(concentration)+\
                    //      QString(" VoltageTemperature= %1V").arg(VoltageTemperature)+QString(" VoltageHydrogen= %1").arg(VoltageHydrogen);

                    str = QString("T=%1℃").arg(temperature)+QString(" C=%1ppm").arg(concentration)+\
                          QString(" VT=%1V").arg(VoltageTemperature)+QString(" VH2=%1V").arg(VoltageHydrogen);

                    if(OneClickState)
                    {
                        quint16 SerialNumber = ui->cRunSerialNumber->value();
                        ui->cRunVoltageTemperature->setValue(VoltageTemperature);
                        ui->cRunVoltageHydrogen->setValue(VoltageHydrogen);
                        OneClickSave_Data(SerialNumber,\
                                          ui->cRunTemperature->value(),\
                                          ui->cRunConcentration->value(),\
                                          VoltageTemperature,VoltageHydrogen);
                        ui->cRunSerialNumber->setValue(++SerialNumber);
                        OneClickState = false;
                    }
                    MessageHint_slot(str);
                }break;
                case HCNG::eCodeType_Device_CalibrationDataLoad_Ack:{MessageHint_slot(QString("Address:%1").arg(address)+QString("号设备校准数据已加载！"));}break;
                case HCNG::eCodeType_Device_CalibrationDataDelete_Ack:{MessageHint_slot(QString("Address:%1").arg(address)+QString("号设备校准数据已删除！"));}break;
                case HCNG::eCodeType_Device_FilePrintf_Ack:{MessageHint_slot(QString("Address:%1").arg(address)+QString("号设备校准文件串口已打印！"));}break;
                case HCNG::eCodeType_Device_FileTransmit_DataAck:
                {
                    quint8 value = *(data + pos++);
                    if(!FileTransmitWrite_user0.enable)break;
                    switch(value)
                    {
                        case 0:
                        {
                            if(FileTransmitWrite_user0.LineNumberResidue)
                            {
                                hcng->Protocol_CommandSend_FileTransmit_DataUser0(ui->cDeviceAddress->text().toUInt(),\
                                                                                  FileTransmitWrite_user0.buffer,\
                                                                                  sizeof(FileTransmitWrite_user0.buffer),\
                                                                                  FileTransmitWrite_user0.LineIndex,\
                                                                                  FileTransmitWrite_user0.LineNumberResidue,\
                                                                                  &FileTransmitWrite_user0.Content[FileTransmitWrite_user0.LineIndex]);

                                    MessageHint_slot(QString("文件正在写入中，请勿操作!")+\
                                                            QString("剩余未发送行：%1")\
                                                            .arg(FileTransmitWrite_user0.LineNumberResidue));
                            }
                            else
                            {
                               hcng->Protocol_CommandSend_FileTransmit_Finish(ui->cDeviceAddress->text().toUInt(),\
                                                                              FileTransmitWrite_user0.buffer,\
                                                                              HCNG::eMemory_FileObject_User0,\
                                                                              FileTransmitWrite_user0.LineNumber);

                                    FileTransmitWrite_user0.enable = false;
                                    MessageHint_slot(QString("等待校准数据拟合！"));
                            }
                        }break;
                        case 1:
                        case 2:{MessageHint_slot("文件写入失败");FileTransmitWrite_user0.enable = false;}break;
                        default:break;
                    }
                }break;
                case HCNG::eCodeType_Device_FileTransmit_FinishAck:
                {
                    quint8 value = *(data + pos++);
                    switch(value)
                    {
                        case 0:{FileTransmitWrite_user0.enable = false;MessageHint_slot("文件写入完成！");}break;
                        case 1:
                        case 2:{MessageHint_slot("文件写入失败");FileTransmitWrite_user0.enable = false;}break;
                        default:break;
                    }

                }break;
                case HCNG::eCodeType_Device_FileTransmit_Data:
                {
                    quint16 object=0,lineindex=0,FrameLineNumber=0,i=0,j=0;

                    HCNG::FileFormatContent_User_StructDef FileFormat_User0;
                    uint8_t *p = NULL;
                    p = (uint8_t *)&FileFormat_User0;

                    object = *(data + pos++);//文件对象                  
                    lineindex = *(data + pos++) << 8;//行索引值,起始值0
                    lineindex |= *(data + pos++);

                    FrameLineNumber = *(data + pos++) << 8;//帧携带行数
                    FrameLineNumber |= *(data + pos++);


                    file.setFileName(dCalibrationFileName);
                    if(file.open(QIODevice::Append | QIODevice::Text))
                    {
                        file_stream.setDevice(&file);
#if dFilemode
                        file_stream.setEncoding(QStringConverter::Utf8);//设置文本流编码，确保中文等非ASCII字符能正确写入
#else
                        file_stream.setCodec("UTF-8");
#endif

                        for(j=0;j<FrameLineNumber;j++)
                        {
                        for(i=0;i<sizeof(FileFormat_User0);i++)
                            {
                                 p[i] = data[pos++];
                            }
                            file_stream << FileFormat_User0.SerialNumber << ","
                                        << FileFormat_User0.temperature << ","
                                        << FileFormat_User0.concentration << ","
                                        << FileFormat_User0.VoltageTemperature << ","
                                        << FileFormat_User0.VoltageHydrogen << "\n";
                        }
                        MessageHint_slot(QString("当前读取行数：%1").arg(lineindex + FrameLineNumber));
                    }
                    file.close();
                }break;
                case HCNG::eCodeType_Device_FileTransmit_Finish:
                {
                    quint16 object=0,lineNum = 0;

                    object = *(data + pos++);
                    lineNum = *(data + pos++) << 8;//帧携带行数
                    lineNum |= *(data + pos++);

                    MessageHint_slot(QString("文件传输完成，完成行数：%1").arg(lineNum));
                }break;
                case HCNG::eCodeType_Device_OperationInformation_Ack:{MessageHint_slot("串口打印状态设置成功！");}
                default:return;
            }
            default:return;
        }break;
    }
}
/****************************************
@function:追加数据到文件
@param:

@return:true--成功；flase--失败
@note:
****************************************/
bool Widget::OneClickSave_Data(quint16 serialnum,float temperature,float concentration,float voltageTemperature,float voltageHydrogen)
{
    file.setFileName(dCalibrationFileName);
    if(file.open(QIODevice::Append | QIODevice::Text))
    {
        file_stream.setDevice(&file);
#if dFilemode
        file_stream.setEncoding(QStringConverter::Utf8);//设置文本流编码，确保中文等非ASCII字符能正确写入
#else
        file_stream.setCodec("UTF-8");
#endif
        file_stream << serialnum << ","
                    << temperature << ","
                    << concentration << ","
                    << voltageTemperature << ","
                    << voltageHydrogen << "\n";
    }
    file.close();
    return true;
}
/****************************************
@function:创建.csv表格
@param:
    FileName：文件名
@return:true--成功；flase--失败
@note:
****************************************/
bool Widget::CreateTabulation(QString FileName)
{
    file.setFileName(FileName);
    if(file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate))
    {
        file_stream.setDevice(&file);
#if dFilemode
        file_stream.setEncoding(QStringConverter::Utf8);//设置文本流编码，确保中文等非ASCII字符能正确写入
#else
        file_stream.setCodec("UTF-8");
#endif

        QStringList HeadFogData = {"序号", "温度/℃", "浓度", "温度检测电压/V","氢气检测电压/V"};
        // 写入表头，CSV格式用逗号分隔
        for (int col = 0; col < HeadFogData.size(); ++col)
        {
            if (col > 0)
            {
                file_stream << ","; // 列之间添加逗号分隔
            }
            file_stream << HeadFogData[col]; // 使用流写入，自动处理类型
        }
        file_stream << "\n"; // 换行
    }
    else {return false;}
    file.close();
    return true;
}
void Widget::on_cScanPort_clicked()
{
    Serial_ReadIPCPort();
}
void Widget::on_cOpenSerialPort_clicked()
{
    emit hcng->Serial_Connected_signal(ui->cSerialBaudRate->currentText(),ui->cSerialPort->currentText());
}
void Widget::on_cCloseSerialPort_clicked()
{
    emit hcng->Serial_DisConnected_signal();
}
void Widget::on_cSystemReste_clicked()
{
    hcng->FrameCommandSend_General(ui->cDeviceAddress->text().toUInt(),\
                                   HCNG::eCommandType_Device,HCNG::eCodeType_Device_SystemReset_Request);
}
void Widget::on_cRunDeviceReset_clicked()
{
    hcng->FrameCommandSend_SystemRestore(HCNG::eDeviceAddress_Broadcast,\
                                         HCNG::eCommandType_Device,\
                                         HCNG::eCodeType_Device_SystemRestore_Request);

}
void Widget::on_cRunDataSample_clicked()
{
    hcng->FrameCommandSend_General( ui->cDeviceAddress->text().toUInt(),\
                                   HCNG::eCommandType_Device,HCNG::eCodeType_Device_Sample_Query);

}
void Widget::on_cRunAddressSet_clicked()
{
    if(!hcng->FrameCommandSend_General_OneValue(ui->cDeviceAddress->text().toUInt(),\
                                                 HCNG::eCommandType_Device,HCNG::eCodeType_Device_Address_Set,\
                                                 ui->cRunAddressText->text().toUInt())){return;}
    else{ui->cDeviceAddress->setText(ui->cRunAddressText->text());}
}


void Widget::on_cClearPrintfWindow_clicked()
{
    ui->cMessagePrintf->clear();
}

void Widget::on_cRunCalibrationDataLoad_clicked()
{
    hcng->FrameCommandSend_General(ui->cDeviceAddress->text().toUInt(),\
                                   HCNG::eCommandType_Device,\
                                   HCNG::eCodeType_Device_CalibrationDataLoad_Request);
}


void Widget::on_cRunCalibrationDataDelete_clicked()
{
    hcng->FrameCommandSend_General_FileObject( ui->cDeviceAddress->text().toUInt(),\
                                              HCNG::eCommandType_Device,HCNG::eCodeType_Device_CalibrationDataDelete_Request,\
                                              HCNG::eMemory_FileObject_VoltageTemperature);
    hcng->FrameCommandSend_General_FileObject( ui->cDeviceAddress->text().toUInt(),\
                                              HCNG::eCommandType_Device,HCNG::eCodeType_Device_CalibrationDataDelete_Request,\
                                              HCNG::eMemory_FileObject_VoltageHydrogen);
}


void Widget::on_cRunCalibrationFileRead_clicked()
{
    hcng->FrameCommandSend_General_FileObject( ui->cDeviceAddress->text().toUInt(),\
                                              HCNG::eCommandType_Device,HCNG::eCodeType_Device_FilePrintf_Request,\
                                              HCNG::eMemory_FileObject_User0);
}

void Widget::on_cRunFileTransmit_Request_clicked()
{
    hcng->FrameCommandSend_General_FileObject( ui->cDeviceAddress->text().toUInt(),\
                                              HCNG::eCommandType_Device,HCNG::eCodeType_Device_FileTransmit_Request,\
                                              HCNG::eMemory_FileObject_User0);
    if(!CreateTabulation(dCalibrationFileName)){MessageHint_slot("csv文件创建失败");return;}//创建文件，并填入表头
}
void Widget::on_cRunFileTransmit_clicked()
{
    FileTransmitWrite_user0.enable = false;
    if(FileRead_User0(FileTransmitWrite_user0) != 0)return;//读取文件至缓冲区
    if(FileTransmitWrite_user0.LineNumber == 0){MessageHint_slot("CalibrationData.csv无数据");return;}

    FileTransmitWrite_user0.enable = true;
    FileTransmitWrite_user0.LineIndex = 0;
    FileTransmitWrite_user0.LineNumberResidue = FileTransmitWrite_user0.LineNumber;
    hcng->Protocol_CommandSend_FileTransmit_DataUser0(ui->cDeviceAddress->text().toUInt(),\
                                                    FileTransmitWrite_user0.buffer,\
                                                    sizeof(FileTransmitWrite_user0.buffer),\
                                                    FileTransmitWrite_user0.LineIndex,\
                                                    FileTransmitWrite_user0.LineNumberResidue,\
                                                    &FileTransmitWrite_user0.Content[FileTransmitWrite_user0.LineIndex]);
}
/****************************************
@function:发送文件内容
@param:
    user0:参数句柄
@return:0--成功；-1--失败
@note:
****************************************/
uint8_t Widget::FileRead_User0(FileTransmitWrite_User0_StructDef &user0)
{
    QFile file;
    QTextStream file_stream;
    uint32_t line = 0;
    QString line_str;
    QByteArray line_buf;
    char val_str[20000] = {0};
    const char *pbuf = NULL;
    HCNG::FileFormatContent_User_StructDef FileFormat;

    user0.LineNumber = 0;//清空行数
    file.setFileName(dCalibrationFileName);
    if(file.open(QIODevice::ReadWrite | QIODevice::Text))
    {
        file_stream.setDevice(&file);
#if dFilemode
        file_stream.setEncoding(QStringConverter::Utf8);//设置文本流编码，确保中文等非ASCII字符能正确写入
#else
        file_stream.setCodec("UTF-8");
#endif
        line = 0;
        while(!file_stream.atEnd())
        {
            line_str = file_stream.readLine();
            if(line_str.isEmpty())break;
            line_buf = line_str.toUtf8();
            pbuf = line_buf.data();
            memset(val_str,0,sizeof(val_str));
            if(line++ == 0){continue;}

            sscanf_s(pbuf,"%hu,%f,%f,%f,%f\n",\
                     &FileFormat.SerialNumber,\
                     &FileFormat.temperature,\
                     &FileFormat.concentration,\
                     &FileFormat.VoltageTemperature,\
                     &FileFormat.VoltageHydrogen);

            //读取数据至缓冲区
            if(user0.LineNumber > sizeof(user0.Content))return -1;
            user0.Content[user0.LineNumber].SerialNumber = user0.LineNumber;
            user0.Content[user0.LineNumber].temperature = FileFormat.temperature;
            user0.Content[user0.LineNumber].concentration = FileFormat.concentration;
            user0.Content[user0.LineNumber].VoltageTemperature = FileFormat.VoltageTemperature;
            user0.Content[user0.LineNumber].VoltageHydrogen = FileFormat.VoltageHydrogen;
            user0.LineNumber++;

            qDebug()<<FileFormat.SerialNumber
                     <<FileFormat.temperature
                     <<FileFormat.concentration
                     <<FileFormat.VoltageTemperature
                     <<FileFormat.VoltageHydrogen;
        }
        file.close();
    }
    else{MessageHint_slot("CalibrationData.csv文件不存在");return -1;}
    return 0;
}

void Widget::on_cRunClearCalibrationFlie_clicked()
{
    if(CreateTabulation(dCalibrationFileName))
    {
        MessageHint_slot("校准文件清空成功！");
        ui->cRunSerialNumber->setValue(0);
    }

}

void Widget::on_cRunOneClickSave_clicked()
{
    hcng->FrameCommandSend_General( ui->cDeviceAddress->text().toUInt(),\
                                   HCNG::eCommandType_Device,HCNG::eCodeType_Device_Sample_Query);
    OneClickState = true;
}






void Widget::on_cRunCalibrationDataBox_clicked()
{
    hcng->FrameCommandSend_General_OneState( ui->cDeviceAddress->text().toUInt(),\
                                            HCNG::eCommandType_Device,HCNG::eCodeType_Device_CalibrationDataUse_Set,\
                                            ui->cRunCalibrationDataBox->checkState());
}


void Widget::on_cRunMessageEnable_clicked()
{
    hcng->FrameCommandSend_General_OneState( ui->cDeviceAddress->text().toUInt(),\
                                            HCNG::eCommandType_Device,HCNG::eCodeType_Device_OperationInformation_Set,\
                                            ui->cRunMessageEnable->checkState());

}

